using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using AuthService.Application.Auth;
using AuthService.Domain.Auth;

namespace AuthService.EntityFrameworkCore.Auth
{
    /// <summary>
    /// 认证会话仓储实现
    /// </summary>
    public class AuthSessionRepository : EfCoreRepository<AuthServiceDbContext, AuthSession, Guid>, IAuthSessionRepository
    {
        public AuthSessionRepository(IDbContextProvider<AuthServiceDbContext> dbContextProvider)
            : base(dbContextProvider)
        {
        }

        public async Task<List<AuthSession>> GetActiveSessionsByUserIdAsync(Guid userId, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => x.UserId == userId && x.IsActive)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<AuthSession> GetBySessionTokenAsync(string sessionToken, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .FirstOrDefaultAsync(x => x.SessionToken == sessionToken, GetCancellationToken(cancellationToken));
        }

        public async Task<List<AuthSession>> GetSessionsByDeviceAsync(string deviceInfo, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => x.DeviceInfo.Contains(deviceInfo))
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<AuthSession>> GetSessionsByIpAsync(string ipAddress, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => x.IpAddress == ipAddress)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<AuthSession>> GetExpiredSessionsAsync(CancellationToken cancellationToken = default)
        {
            var now = DateTime.Now;
            return await DbSet
                .Where(x => x.ExpiresAt < now)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task RevokeAllSessionsByUserIdAsync(Guid userId, CancellationToken cancellationToken = default)
        {
            var sessions = await GetActiveSessionsByUserIdAsync(userId, cancellationToken);
            foreach (var session in sessions)
            {
                session.IsActive = false;
                session.LogoutTime = DateTime.Now;
                session.LogoutReason = "Session revoked by admin";
            }
            await SaveChangesAsync(cancellationToken);
        }

        public async Task RevokeSessionAsync(Guid sessionId, string reason = "", CancellationToken cancellationToken = default)
        {
            var session = await GetAsync(sessionId, cancellationToken: cancellationToken);
            if (session != null)
            {
                session.IsActive = false;
                session.LogoutTime = DateTime.Now;
                session.LogoutReason = reason;
                await UpdateAsync(session, autoSave: true, cancellationToken: cancellationToken);
            }
        }

        public async Task<int> CleanupExpiredSessionsAsync(CancellationToken cancellationToken = default)
        {
            var expiredSessions = await GetExpiredSessionsAsync(cancellationToken);
            if (expiredSessions.Any())
            {
                await DeleteManyAsync(expiredSessions, autoSave: true, cancellationToken: cancellationToken);
            }
            return expiredSessions.Count;
        }

        public async Task<int> GetActiveSessionCountAsync(Guid? userId = null, CancellationToken cancellationToken = default)
        {
            var query = DbSet.Where(x => x.IsActive);
            if (userId.HasValue)
            {
                query = query.Where(x => x.UserId == userId.Value);
            }
            return await query.CountAsync(cancellationToken);
        }

        public async Task<bool> IsSessionValidAsync(string sessionToken, CancellationToken cancellationToken = default)
        {
            var session = await GetBySessionTokenAsync(sessionToken, cancellationToken);
            return session != null && session.IsActive && !session.IsExpired();
        }

        public async Task ExtendSessionAsync(Guid sessionId, TimeSpan extendTime, CancellationToken cancellationToken = default)
        {
            var session = await GetAsync(sessionId, cancellationToken: cancellationToken);
            if (session != null && session.IsActive)
            {
                session.ExpiresAt = DateTime.Now.Add(extendTime);
                session.LastActivityTime = DateTime.Now;
                await UpdateAsync(session, autoSave: true, cancellationToken: cancellationToken);
            }
        }

        public async Task<AuthSession> GetByAccessTokenIdAsync(string accessTokenId, CancellationToken cancellationToken = default)
        {
            if (Guid.TryParse(accessTokenId, out var guid))
            {
                return await DbSet
                    .FirstOrDefaultAsync(x => x.AccessTokenId == guid, GetCancellationToken(cancellationToken));
            }
            return null;
        }

        public async Task<List<AuthSession>> GetByDeviceIdAsync(string deviceId, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => x.DeviceId == deviceId)
                .OrderByDescending(x => x.CreatedAt)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<int> CleanupExpiredSessionsCountAsync(CancellationToken cancellationToken = default)
        {
            var expiredSessions = await GetExpiredSessionsAsync(cancellationToken);
            if (expiredSessions.Any())
            {
                await DeleteManyAsync(expiredSessions, autoSave: true, cancellationToken: cancellationToken);
            }
            return expiredSessions.Count;
        }

        public async Task RevokeSessionsByDeviceIdAsync(string deviceId, CancellationToken cancellationToken = default)
        {
            var sessions = await DbSet
                .Where(x => x.DeviceId == deviceId && x.IsActive)
                .ToListAsync(GetCancellationToken(cancellationToken));

            foreach (var session in sessions)
            {
                session.IsActive = false;
                session.LogoutTime = DateTime.Now;
                session.LogoutReason = "Device revoked";
            }

            await SaveChangesAsync(cancellationToken);
        }

        public async Task<AuthSessionStatisticsDto> GetSessionStatisticsAsync(Guid userId, CancellationToken cancellationToken = default)
        {
            var sessions = await DbSet
                .Where(x => x.UserId == userId)
                .ToListAsync(GetCancellationToken(cancellationToken));

            return new AuthSessionStatisticsDto
            {
                TotalSessions = sessions.Count,
                ActiveSessions = sessions.Count(x => x.IsActive && x.ExpiresAt > DateTime.Now),
                ExpiredSessions = sessions.Count(x => x.ExpiresAt <= DateTime.Now),
                RevokedSessions = sessions.Count(x => !x.IsActive || x.LogoutTime.HasValue),
                LastLoginTime = sessions.OrderByDescending(x => x.CreatedAt).FirstOrDefault()?.CreatedAt,
                DeviceCount = sessions.Select(x => x.DeviceId).Distinct().Count()
            };
        }
    }
}